﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using iNet.Media.Imaging;
using iNet.Media.Imaging.Exif;
using iNet.Media.Xmp.MicrosoftPhoto;

namespace iNet.Media.Xmp
{
	/// <summary>
	/// XMP 影像中繼資料。
	/// </summary>
	public class XmpImageMetadata : XmpMetadata, IImageMetadata
	{
		#region 靜態欄位
		/// <summary>
		/// 適用於 IImageMetadata.Aperture 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> AperturePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "FNumber"),
		});
		/// <summary>
		/// 適用於 IMetadata.Author 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> AuthorPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.AuthorPropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.Tiff, "Artist"),
			}))
		);
		/// <summary>
		/// 適用於 IImageMetadata.CameraManufacturer 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> CameraManufacturerPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Tiff, "Make"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.CameraModel 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> CameraModelPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Tiff, "Model"),
		});
		/// <summary>
		/// 適用於 IMetadata.City 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> CityPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Photoshop, "City"),
		});
		/// <summary>
		/// 適用於 IMetadata.Comment 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> CommentPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "UserComment"),
		});
		/// <summary>
		/// 適用於 IMetadata.Copyright 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> CopyrightPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.CopyrightPropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.Tiff, "Copyright"),
			}))
		);
		/// <summary>
		/// 適用於 IMetadata.CountryRegion 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> CountryRegionPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Photoshop, "Country"),
		});
		/// <summary>
		/// 適用於 IMetadata.CreationTime 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> CreationTimePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.CreationTimePropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "DateTimeOriginal"),
				new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "DateTimeDigitized"),
				new XmpPropertyName(CommonXmpSchemas.Photoshop, "DateCreated"),
			}))
		);
		/// <summary>
		/// 適用於 IImageMetadata.ExposureCompensation 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> ExposureCompensationPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.CameraRaw, "Exposure"),
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "ExposureBiasValue"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.ExposureProgram 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> ExposureProgramPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "ExposureProgram"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.FlashManufacturer 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> FlashManufacturerPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto, "FlashManufacturer"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.FlashModel 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> FlashModelPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto, "FlashModel"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.FlashState 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> FlashStatePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "Flash"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.FocalLength 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> FocalLengthPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "FocalLength"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.FocalLengthIn35mmFilm 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> FocalLengthIn35mmFilmPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "FocalLengthIn35mmFilm"),
		});
		static readonly XmpPropertyName[] GpsCoordinatePropertyNames = new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "GPSAltitude"),
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "GPSAltitudeRef"),
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "GPSLatitude"),
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "GPSLongitude"),
		};
		static readonly Regex _GpsCoordinateRegex = new Regex(@"^(?<Degrees>[0-9]{3})\,(?<Minutes>[0-9]{2})(?<SecondType>[\,\.])(?<Seconds>[0-9]{2})(?<Direction>[NSEW])$");
		/// <summary>
		/// 適用於 IImageMetadata.ISO 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> ISOPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "ISOSpeedRatings"),
		});
		/// <summary>
		/// 適用於 IMetadata.LastWriteTime 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> LastWriteTimePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.LastWriteTimePropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.Tiff, "DateTime"),
			}))
		);
		/// <summary>
		/// 適用於 IImageMetadata.LensManufacturer 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> LensManufacturerPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto, "LensManufacturer"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.LensModel 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> LensModelPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto, "LensModel"),
			new XmpPropertyName(CommonXmpSchemas.ExifAdditional, "Lens"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.Orientation 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> OrientationPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Tiff, "Orientation"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.People 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> PeoplePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto_1_2, "RegionInfo"),
		});
		/// <summary>
		/// 適用於 IMetadata.RatingPercentage 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> RatingPercentagePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.MicrosoftPhoto, "Rating"),
		});
		/// <summary>
		/// 適用於 IImageMetadata.ShutterSpeed 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> ShutterSpeedPropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.ExifSpecific, "ShutterSpeedValue"),
		});
		/// <summary>
		/// 適用於 IMetadata.Software 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> SoftwarePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.SoftwarePropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.Tiff, "Software"),
			}))
		);
		/// <summary>
		/// 適用於 IMetadata.StateProvince 屬性的 XMP Property 名稱。
		/// </summary>
		public static readonly ReadOnlyCollection<XmpPropertyName> StateProvincePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new XmpPropertyName[]{
			new XmpPropertyName(CommonXmpSchemas.Photoshop, "State"),
		});
		/// <summary>
		/// 適用於 IMetadata.Title 屬性的 XMP Property 名稱。
		/// </summary>
		public static new readonly ReadOnlyCollection<XmpPropertyName> TitlePropertyNames = new ReadOnlyCollection<XmpPropertyName>(new List<XmpPropertyName>(
			XmpMetadata.TitlePropertyNames.Concat<XmpPropertyName>(new XmpPropertyName[]{
				new XmpPropertyName(CommonXmpSchemas.Tiff, "ImageDescription"),
			}))
		);
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 XmpImageMetadata 執行個體。
		/// </summary>
		public XmpImageMetadata()
		{ }
		/// <summary>
		/// 初始化新的 XmpImageMetadata 執行個體。
		/// </summary>
		/// <param name="properties">XMP Property 列表。</param>
		/// <param name="isReadOnly">是否為唯讀。</param>
		public XmpImageMetadata(IEnumerable<XmpProperty> properties, bool isReadOnly)
			: base(properties, isReadOnly)
		{ }
		/// <summary>
		/// 初始化新的 XmpImageMetadata 執行個體。
		/// </summary>
		/// <param name="template">作為範本的中繼資料。</param>
		/// <param name="isReadOnly">是否為唯讀。</param>
		public XmpImageMetadata(IMetadata template, bool isReadOnly)
			: base(template, isReadOnly)
		{ }
		#endregion
		#region Aperture
		/// <summary>
		/// 取得或設定光圈值。
		/// </summary>
		public virtual double? Aperture
		{
			get
			{
				URational? value = this.GetValue<URational?>(this.FindProperties(AperturePropertyNames));
				if (value != null)
					return value.Value.ToDouble();
				return null;
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null && !double.IsNaN(value.Value))
				{
					if (value.Value < 0)
						throw new ArgumentOutOfRangeException();
					this.UpdateValue(AperturePropertyNames, AperturePropertyNames[0], Rational.FromDouble(value.Value));
				}
				else
					this.RemoveProperties(AperturePropertyNames);
			}
		}
		#endregion
		#region Authors
		/// <summary>
		/// 取得或設定影像的作者。
		/// </summary>
		public override string[] Authors
		{
			get
			{
				return this.GetStringArray(this.FindProperties(AuthorPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(AuthorPropertyNames, AuthorPropertyNames[0], value, XmpArrayType.Ordered);
			}
		}
		#endregion
		#region CameraManufacturer
		/// <summary>
		/// 取得或設定相機製造商。
		/// </summary>
		public virtual string CameraManufacturer
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CameraManufacturerPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CameraManufacturerPropertyNames, CameraManufacturerPropertyNames[0], value);
			}
		}
		#endregion
		#region CameraModel
		/// <summary>
		/// 取得或設定相機型號。
		/// </summary>
		public virtual string CameraModel
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CameraModelPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CameraModelPropertyNames, CameraModelPropertyNames[0], value);
			}
		}
		#endregion
		#region City
		/// <summary>
		/// 取得或設定影像所在的城市。
		/// </summary>
		public override string City
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CityPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CityPropertyNames, CityPropertyNames[0], value);
			}
		}
		#endregion
		#region Clone
		/// <summary>
		/// 建立可修改的 XmpImageMetadata 複本。
		/// </summary>
		/// <returns>XmpImageMetadata 複本。</returns>
		public override object Clone()
		{
			return new XmpImageMetadata(this, false);
		}
		#endregion
		#region Comment
		/// <summary>
		/// 取得或設定影像的註解文字。
		/// </summary>
		public override string Comment
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CommentPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CommentPropertyNames, CommentPropertyNames[0], value);
			}
		}
		#endregion
		#region ConvertFromGpsCoordinate
		double? ConvertFromGpsCoordinate(XmpProperty property)
		{
			//初步確認
			if (property == null || !(property.Value is TextXmpPropertyValue))
				return null;

			//比對字串
			Match match = _GpsCoordinateRegex.Match((property.Value as TextXmpPropertyValue).Value);
			if (!match.Success)
				return null;

			//取得度數
			double coordinate = double.Parse(match.Groups["Degrees"].Value);

			//取得分
			coordinate += (double.Parse(match.Groups["Minutes"].Value) / 60);

			//取得秒
			if (match.Groups["SecondType"].Value == ".")
				coordinate += (double.Parse(match.Groups["Seconds"].Value) / 6000);
			else
				coordinate += (double.Parse(match.Groups["Seconds"].Value) / 3600);

			//取得方向
			switch (match.Groups["Direction"].Value)
			{
				case "S":
				case "W":
					coordinate = -coordinate;
					break;
			}

			//轉換完成
			return coordinate;
		}
		#endregion
		#region ConvertToGpsCoordinate
		XmpPropertyValue ConvertToGpsCoordinate(double coordinate, bool isLatitude)
		{
			//確認參數
			if (isLatitude)
			{
				if (coordinate > 90 || coordinate < -90)
					throw new ArgumentOutOfRangeException();
			}
			else
			{
				if (coordinate > 180 || coordinate < -180)
					throw new ArgumentOutOfRangeException();
			}

			//取得方向
			char direction;
			if (isLatitude)
				direction = (coordinate >= 0 ? 'N' : 'S');
			else
				direction = (coordinate >= 0 ? 'E' : 'W');

			//取得度/分/秒
			coordinate = Math.Abs(coordinate);
			int degrees = (int)coordinate;
			coordinate -= degrees;
			int minutes = (int)(coordinate * 60);
			coordinate -= (minutes / 60.0);
			int seconds = (int)(coordinate * 3600);

			//轉成 XMP Property 值
			return new TextXmpPropertyValue(string.Format("{0:00},{1:00},{2:00}{3}", degrees, minutes, seconds, direction));
		}
		#endregion
		#region CopyFrom
		/// <summary>
		/// 從指定的中繼資料複製屬性。
		/// </summary>
		/// <param name="metadata">指定的中繼資料。</param>
		/// <exception cref="InvalidOperationException">中繼資料為唯讀。</exception>
		public override void CopyFrom(IMetadata metadata)
		{
			//確認狀態
			if (this.IsReadOnly)
				throw new InvalidOperationException();

			//呼叫基底
			base.CopyFrom(metadata);

			//複製屬性
			if (metadata != null && metadata != this)
			{
				//複製一般屬性
				this.Comment = metadata.Comment;
				this.CountryRegion = metadata.CountryRegion;
				this.StateProvince = metadata.StateProvince;

				//複製影像屬性
				IImageMetadata imageMetadata = (metadata as IImageMetadata);
				if (imageMetadata != null)
				{
					this.Aperture = imageMetadata.Aperture;
					this.CameraManufacturer = imageMetadata.CameraManufacturer;
					this.CameraModel = imageMetadata.CameraManufacturer;
					this.ExposureCompensation = imageMetadata.ExposureCompensation;
					this.ExposureProgram = imageMetadata.ExposureProgram;
					this.FlashManufacturer = imageMetadata.FlashManufacturer;
					this.FlashModel = imageMetadata.FlashModel;
					this.FlashState = imageMetadata.FlashState;
					this.FocalLength = imageMetadata.FocalLength;
					this.FocalLengthIn35mmFilm = imageMetadata.FocalLengthIn35mmFilm;
					this.GeoCoordinate = imageMetadata.GeoCoordinate;
					this.ISO = imageMetadata.ISO;
					this.LensManufacturer = imageMetadata.LensManufacturer;
					this.LensModel = imageMetadata.LensModel;
					this.Orientation = imageMetadata.Orientation;
					this.People = imageMetadata.People;
					this.ShutterSpeed = imageMetadata.ShutterSpeed;
				}
			}
		}
		#endregion
		#region Copyright
		/// <summary>
		/// 取得或設定影像的版權資訊。
		/// </summary>
		public override string Copyright
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CopyrightPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CopyrightPropertyNames, CopyrightPropertyNames[0], value);
			}
		}
		#endregion
		#region CountryRegion
		/// <summary>
		/// 取得或設定影像所在的國家或地區。
		/// </summary>
		public override string CountryRegion
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(CountryRegionPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CountryRegionPropertyNames, CountryRegionPropertyNames[0], value);
			}
		}
		#endregion
		#region CreationTime
		/// <summary>
		/// 取得或設定影像建立時間。
		/// </summary>
		public override DateTime? CreationTime
		{
			get
			{
				return this.GetValue<DateTime?>(this.FindProperties(CreationTimePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(CreationTimePropertyNames, CreationTimePropertyNames[0], value);
			}
		}
		#endregion
		#region ExposureCompensation
		/// <summary>
		/// 取得或設定曝光補償值。
		/// </summary>
		public virtual double? ExposureCompensation
		{
			get
			{
				return this.GetValue<double?>(this.FindProperties(ExposureCompensationPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(ExposureCompensationPropertyNames, ExposureCompensationPropertyNames[0], Rational.FromDouble(value.Value));
				else
					this.RemoveProperties(ExposureCompensationPropertyNames);
			}
		}
		#endregion
		#region ExposureProgram
		/// <summary>
		/// 取得或設定曝光程式。
		/// </summary>
		public virtual ExposureProgram? ExposureProgram
		{
			get
			{
				int? value = this.GetValue<int?>(this.FindProperties(ExposureProgramPropertyNames));
				return (value != null ? (ExposureProgram?)(ExposureProgram)value.Value : null);
			}
			set
			{
 				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(ExposureProgramPropertyNames, ExposureProgramPropertyNames[0], (int)value.Value);
				else
					this.RemoveProperties(ExposureProgramPropertyNames);
			}
		}
		#endregion
		#region FlashManufacturer
		/// <summary>
		/// 取得或設定閃光燈製造商。
		/// </summary>
		public string FlashManufacturer
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(FlashManufacturerPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(FlashManufacturerPropertyNames, FlashManufacturerPropertyNames[0], value);
			}
		}
		#endregion
		#region FlashModel
		/// <summary>
		/// 取得或設定閃光燈型號。
		/// </summary>
		public string FlashModel
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(FlashModelPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(FlashModelPropertyNames, FlashModelPropertyNames[0], value);
			}
		}
		#endregion
		#region FlashState
		/// <summary>
		/// 取得或設定閃光燈狀態。
		/// </summary>
		public virtual FlashState? FlashState
		{
			get
			{
				XmpProperty property = this.FindProperty(FlashStatePropertyNames[0]);
				if (property == null)
					return null;

				//取得 Property 值
				FlashXmpPropertyValue value = (property.Value as FlashXmpPropertyValue);
				if (value == null)
					return null;

				//取得屬性
				return Imaging.FlashState.FromXmpFlashState(value);
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
				{
					FlashXmpPropertyValue propertyValue = value.Value.ToXmpFlashState();
					this.UpdateValue(FlashStatePropertyNames, FlashStatePropertyNames[0], propertyValue);
				}
				else
					this.RemoveProperties(FlashStatePropertyNames);
			}
		}
		#endregion
		#region FocalLength
		/// <summary>
		/// 取得或設定實際焦距，單位為毫米 (mm)。
		/// </summary>
		public virtual double? FocalLength
		{
			get
			{
				return this.GetValue<double?>(this.FindProperties(FocalLengthPropertyNames));
			}
			set
			{
 				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(FocalLengthPropertyNames, FocalLengthPropertyNames[0], Rational.FromDouble(value.Value));
				else
					this.RemoveProperties(FocalLengthPropertyNames);
			}
		}
		#endregion
		#region FocalLengthIn35mmFilm
		/// <summary>
		/// 取得或設定等效焦距，單位為毫米 (mm)。
		/// </summary>
		public virtual double? FocalLengthIn35mmFilm
		{
			get
			{
				return this.GetValue<double?>(this.FindProperties(FocalLengthIn35mmFilmPropertyNames));
			}
			set
			{
 				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(FocalLengthIn35mmFilmPropertyNames, FocalLengthIn35mmFilmPropertyNames[0], Rational.FromDouble(value.Value));
				else
					this.RemoveProperties(FocalLengthIn35mmFilmPropertyNames);
			}
		}
		#endregion
		#region GeoCoordinate
		/// <summary>
		/// 取得或設定影像的地理座標。
		/// </summary>
		public virtual GeoCoordinate GeoCoordinate
		{
			get
			{
				//搜尋 XMP Property
				XmpProperty altProperty = null, altRefProperty = null, latProperty = null, lngProperty = null;
				for (int i = this.XmpProperties.Count - 1; i >= 0; --i)
				{
					XmpProperty property = this.XmpProperties[i];
					if (property == null)
						continue;
					if (property.Name.Equals(GpsCoordinatePropertyNames[0]))
						altProperty = property;
					else if (property.Name.Equals(GpsCoordinatePropertyNames[1]))
						altRefProperty = property;
					else if (property.Name.Equals(GpsCoordinatePropertyNames[2]))
						latProperty = property;
					else if (property.Name.Equals(GpsCoordinatePropertyNames[3]))
						lngProperty = property;
				}
				if (altProperty == null && latProperty == null && lngProperty == null)
					return null;

				//取得平面座標
				double? lat = this.ConvertFromGpsCoordinate(latProperty);
				double? lng = this.ConvertFromGpsCoordinate(lngProperty);
				GeoCoordinate coordinate = new GeoCoordinate();
				if (lat != null)
				{
					try
					{
						coordinate.Latitude = lat.Value;
					}
					catch
					{ }
				}
				if (lng != null)
				{
					try
					{
						coordinate.Latitude = lng.Value;
					}
					catch
					{ }
				}

				//取得高度
				if (altProperty != null && altRefProperty != null && altProperty.Value is RationalXmpPropertyValue && altRefProperty.Value is IntegerXmpPropertyValue)
				{
					coordinate.Altitude = (altProperty.Value as RationalXmpPropertyValue).Value.ToDouble();
					if ((altRefProperty.Value as IntegerXmpPropertyValue).Value != 0)
						coordinate.Altitude = -coordinate.Altitude;
				}

				//回傳
				return (coordinate.IsUnknown ? null : coordinate);
			}
			set
			{
				//確認狀態
				if (this.IsReadOnly)
					throw new InvalidOperationException();

				//移除 XMP Property
				this.RemoveProperties(GpsCoordinatePropertyNames);

				//建立 XMP Property
				if (value != null && !value.IsUnknown)
				{
					//建立經緯度資訊
					double lat = value.Latitude;
					double lng = value.Longitude;
					if (!double.IsNaN(lat))
						this.AddProperty(GpsCoordinatePropertyNames[2], this.ConvertToGpsCoordinate(lat, true));
					if (!double.IsNaN(lng))
						this.AddProperty(GpsCoordinatePropertyNames[3], this.ConvertToGpsCoordinate(lng, false));

					//建立高度資訊
					double alt = value.Altitude;
					if (!double.IsNaN(alt))
					{
						this.AddProperty(GpsCoordinatePropertyNames[0], new RationalXmpPropertyValue(Rational.FromDouble(Math.Abs(alt))));
						this.AddProperty(GpsCoordinatePropertyNames[1], new IntegerXmpPropertyValue(alt >= 0 ? 0 : 1));
					}
				}
			}
		}
		#endregion
		#region GetAsReadOnly
		/// <summary>
		/// 取得唯讀的 XmpImageMetadata。
		/// </summary>
		/// <returns>唯讀的 XmpImageMetadata。</returns>
		public override IMetadata GetAsReadOnly()
		{
			if (this.IsReadOnly)
				return this;
			return new XmpImageMetadata(this, true);
		}
		#endregion
		#region ISO
		/// <summary>
		/// 取得或設定 ISO 值。
		/// </summary>
		public virtual int? ISO
		{
			get
			{
				//搜尋 XMP Property
				XmpProperty property = this.FindProperty(ISOPropertyNames[0]);
				if (property == null)
					return null;

				//取得 ISO 值
				ArrayXmpPropertyValue value = (property.Value as ArrayXmpPropertyValue);
				if (value == null || value.Items.Count == 0)
					return null;
				IntegerXmpPropertyValue isoValue = (value.Items[0] as IntegerXmpPropertyValue);
				return (isoValue != null ? (int?)isoValue.Value : null);
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
				{
					ArrayXmpPropertyValue arrayValue = new ArrayXmpPropertyValue(XmpArrayType.Ordered);
					arrayValue.Items.Add(new IntegerXmpPropertyValue(value.Value));
					this.UpdateValue(ISOPropertyNames, ISOPropertyNames[0], arrayValue);
				}
				else
					this.RemoveProperties(ISOPropertyNames);
			}
		}
		#endregion
		#region LastWriteTime
		/// <summary>
		/// 取得或設定影像最近更新時間。
		/// </summary>
		public override DateTime? LastWriteTime
		{
			get
			{
				return this.GetValue<DateTime?>(this.FindProperties(LastWriteTimePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(LastWriteTimePropertyNames, LastWriteTimePropertyNames[0], value);
			}
		}
		#endregion
		#region LensManufacturer
		/// <summary>
		/// 取得或設定鏡頭製造商。
		/// </summary>
		public string LensManufacturer
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(LensManufacturerPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(LensManufacturerPropertyNames, LensManufacturerPropertyNames[0], value);
			}
		}
		#endregion
		#region LensModel
		/// <summary>
		/// 取得或設定鏡頭型號。
		/// </summary>
		public string LensModel
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(LensModelPropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(LensModelPropertyNames, LensModelPropertyNames[0], value);
			}
		}
		#endregion
		#region Orientation
		/// <summary>
		/// 取得或設定影像方向。
		/// </summary>
		public virtual ImageOrientation? Orientation
		{
			get
			{
				int? value = this.GetValue<int?>(this.FindProperties(OrientationPropertyNames));
				return (value != null ? (ImageOrientation?)(ImageOrientation)value.Value : null);
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(OrientationPropertyNames, OrientationPropertyNames[0], (int)value.Value);
				else
					this.RemoveProperties(OrientationPropertyNames);
			}
		}
		#endregion
		#region People
		/// <summary>
		/// 取得或設定影像中的人員資訊。
		/// </summary>
		public virtual PersonRegion[] People
		{
			get
			{
				//搜尋 XMP Property
				XmpProperty property = this.FindProperty(PeoplePropertyNames[0]);
				if (property == null)
					return null;

				//取得 RegionInfo
				RegionInfoXmpPropertyValue regionInfo = (property.Value as RegionInfoXmpPropertyValue);
				if (regionInfo == null || regionInfo.Regions == null)
					return null;

				//取得所有人員資訊
				List<PersonRegion> people = new List<PersonRegion>();
				ArrayXmpPropertyValue regionValueArray = regionInfo.Regions;
				for (int i = 0, count = regionValueArray.Items.Count; i < count; ++i)
				{
					//取得資料結構
					RegionXmpPropertyValue regionValue = (regionValueArray.Items[i] as RegionXmpPropertyValue);
					if (regionValue == null)
						continue;

					//取得人員資訊
					TextXmpPropertyValue nameValue = regionValue.PersonDisplayName;
					TextXmpPropertyValue rectValue = regionValue.Rectangle;
					if (nameValue == null || nameValue.Value == null)
						continue;
					Rect rect;
					if (rectValue != null && rectValue.Value != null)
					{
						string[] bounds = rectValue.Value.Split(',');
						double left, top, width, height;
						if (bounds.Length != 4 ||
							!double.TryParse(bounds[0], out left) ||
							!double.TryParse(bounds[1], out top) ||
							!double.TryParse(bounds[2], out width) ||
							!double.TryParse(bounds[3], out height) ||
							top < 0 || top > 1 ||
							left < 0 || left > 1 ||
							height < 0 || height > 1 ||
							width < 0 || width > 1)
							rect = Rect.Empty;
						else
							rect = new Rect(left, top, width, height);
					}
					else
						rect = Rect.Empty;

					//建立資料結構
					people.Add(new PersonRegion(rect, nameValue.Value, null));
				}

				//回傳
				return people.ToArray();
			}
			set
			{
				//確認狀態
				if (this.IsReadOnly)
					throw new InvalidOperationException();

				//更新屬性
				this.RemoveProperties(PeoplePropertyNames);
				if (value != null && value.Length > 0)
				{
					//轉成 Region XMP Property Value
					ArrayXmpPropertyValue regionValues = new ArrayXmpPropertyValue(XmpArrayType.Unordered);
					for (int i = 0; i < value.Length; ++i)
					{
						PersonRegion person = value[i];
						if (person != null)
						{
							RegionXmpPropertyValue regionValue = new RegionXmpPropertyValue();
							if (person.Name != null)
								regionValue.PersonDisplayName = new TextXmpPropertyValue(person.Name);
							Rect rect = person.Rectangle;
							regionValue.Rectangle = new TextXmpPropertyValue(string.Format("{0:0.000000}, {1:0.000000}, {2:0.000000}, {3:0.000000}", rect.X, rect.Y, rect.Width, rect.Height));
							regionValues.Items.Add(regionValue);
						}
					}

					//建立 RegionInfo XMP Property Value
					if (regionValues.Items.Count == 0)
						return;
					RegionInfoXmpPropertyValue regionInfoValue = new RegionInfoXmpPropertyValue();
					regionInfoValue.Regions = regionValues;

					//更新 XMP Property
					this.AddProperty(PeoplePropertyNames[0], regionInfoValue);
				}
			}
		}
		#endregion
		#region RatingPercentage
		/// <summary>
		/// 取得或設定影像的評價百分比。
		/// </summary>
		public override int? RatingPercentage
		{
			get
			{
				return this.GetValue<int?>(this.FindProperties(RatingPercentagePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(RatingPercentagePropertyNames, RatingPercentagePropertyNames[0], value);
			}
		}
		#endregion
		#region ShutterSpeed
		/// <summary>
		/// 取得或設定快門速度。
		/// </summary>
		public virtual URational? ShutterSpeed
		{
			get
			{
				Rational? value = this.GetValue<Rational?>(this.FindProperties(ShutterSpeedPropertyNames));
				if (value != null)
				{
					try
					{
						//約分
						URational shutterSpeed = (URational)value.Value;
						if (shutterSpeed.Numerator <= shutterSpeed.Denominator && shutterSpeed.Numerator != 1 && (shutterSpeed.Denominator % shutterSpeed.Numerator) == 0)
						{
							shutterSpeed.Denominator /= shutterSpeed.Numerator;
							shutterSpeed.Numerator = 1;
						}

						//完成
						return shutterSpeed;
					}
					catch
					{ }
				}
				return null;
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				if (value != null)
					this.UpdateValue(ShutterSpeedPropertyNames, ShutterSpeedPropertyNames[0], (Rational)value.Value);
				else
					this.RemoveProperties(ShutterSpeedPropertyNames);
			}
		}
		#endregion
		#region Software
		/// <summary>
		/// 取得或設定製作影像的軟體名稱。
		/// </summary>
		public override string Software
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(SoftwarePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(SoftwarePropertyNames, SoftwarePropertyNames[0], value);
			}
		}
		#endregion
		#region StateProvince
		/// <summary>
		/// 取得或設定影像所在的省或州。
		/// </summary>
		public override string StateProvince
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(StateProvincePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(StateProvincePropertyNames, StateProvincePropertyNames[0], value);
			}
		}
		#endregion
		#region Title
		/// <summary>
		/// 取得或設定影像的標題。
		/// </summary>
		public override string Title
		{
			get
			{
				return this.GetValue<string>(this.FindProperties(TitlePropertyNames));
			}
			set
			{
				if (this.IsReadOnly)
					throw new InvalidOperationException();
				this.UpdateValue(TitlePropertyNames, TitlePropertyNames[0], value);
			}
		}
		#endregion
	}
}
